package com.example.project.utils.file;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Optional;

import javax.imageio.ImageIO;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.project.config.ProjectConfig;
import com.example.project.constant.Constants;
import com.example.project.utils.DateUtils;
import com.example.project.utils.StringUtils;
import com.example.project.utils.uuid.IdUtils;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * 文件处理工具类
 * 
 * @author ruoyi
 */
public class FileUtils {

	private static final Logger logger = LoggerFactory.getLogger(FileUtils.class);

	public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";

    /**
	 * 检查文件是否可读 类似Ruby中的file_readable方法
	 */
	public static boolean isFileReadable(String filename) {
		if (StringUtils.isEmpty(filename)) {
			return false;
		}

		File file = new File(filename);
		return file.exists() && file.canRead();
	}
    
    /**
     * 检查是否是差异文件
     * 类似Ruby中的@attachment.is_diff?
     */
    public static boolean isDiffFile(String fileName) {
        if (StringUtils.isNotEmpty(fileName)) {
            return false;
        }
        
        String filename = fileName.toLowerCase();
        return filename.endsWith(".diff") || filename.endsWith(".patch");
    }
    
    /**
     * 检查是否是文本文件
     * 类似Ruby中的@attachment.is_text?
     */
    public static boolean isTextFile(String type) {
        if (StringUtils.isNotEmpty(type)) {
            return false;
        }
        
        String contentType = type.toLowerCase();
        return contentType.startsWith("text/") || 
               contentType.contains("xml") || 
               contentType.contains("json") ||
               contentType.contains("javascript");
    }
    
    /**
     * 检查是否是图片文件
     * 类似Ruby中的@attachment.is_image?
     */
    public static boolean isImageFile(String type) {
        if (StringUtils.isNotEmpty(type)) {
            return false;
        }
        
        String contentType = type.toLowerCase();
        return contentType.startsWith("image/");
    }
    
    /**
     * 检查是否是PDF文件
     * 类似Ruby中的@attachment.is_pdf?
     */
    public static boolean isPdfFile(String contentType) {
        if (StringUtils.isNotEmpty(contentType)) {
            return false;
        }
        
        return contentType.equalsIgnoreCase("application/pdf");
    }
    
    /**
     * 读取差异文件内容
     * 类似Ruby中的File.read(@attachment.diskfile)
     */
    public static String readFileContent(String filePath) {
        try {
            File file = new File(filePath);
            return new String(Files.readAllBytes(file.toPath()));
        } catch (IOException e) {
            logger.error("Failed to read diff content: {}", e.getMessage());
            return "";
        }
    }
    
    /**
     * 生成缩略图
     * 类似Ruby中的@attachment.thumbnail(:size => params[:size])
     */
    public static Optional<File> generateThumbnail(String filePath, Integer size) {
        if (StringUtils.isNotEmpty(filePath)) {
            return Optional.empty();
        }
        
        try {
            // 这里需要实现缩略图生成逻辑
            // 可以使用ImageIO或第三方库如Thumbnailator
            File originalFile = new File(filePath);
            File thumbnailFile = createThumbnailFile(originalFile, size);
            
            return Optional.of(thumbnailFile);
        } catch (Exception e) {
            logger.error("Failed to generate thumbnail: {}", e.getMessage());
            return Optional.empty();
        }
    }
    
    /**
     * 创建缩略图文件
     */
    private static File createThumbnailFile(File originalFile, Integer size) throws IOException {
        if (size == null || size <= 0) {
            size = 200; // 默认缩略图大小
        }
        
        // 检查是否为图片文件
        if (!isImageFile(getFileContentType(originalFile))) {
            throw new IOException("File is not an image: " + originalFile.getName());
        }
        
        // 读取原始图片
        BufferedImage originalImage = ImageIO.read(originalFile);
        if (originalImage == null) {
            throw new IOException("Unable to read image file: " + originalFile.getName());
        }
        
        // 计算缩略图尺寸
        int originalWidth = originalImage.getWidth();
        int originalHeight = originalImage.getHeight();
        int thumbnailWidth, thumbnailHeight;
        
        if (originalWidth > originalHeight) {
            // 宽图
            thumbnailWidth = size;
            thumbnailHeight = (int) (originalHeight * ((double) size / originalWidth));
        } else {
            // 高图或方图
            thumbnailHeight = size;
            thumbnailWidth = (int) (originalWidth * ((double) size / originalHeight));
        }
        
        // 确保最小尺寸为1
        thumbnailWidth = Math.max(thumbnailWidth, 1);
        thumbnailHeight = Math.max(thumbnailHeight, 1);
        
        // 创建缩略图
        BufferedImage thumbnailImage = new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = thumbnailImage.createGraphics();
        
        // 设置渲染质量
        g.setRenderingHint(java.awt.RenderingHints.KEY_INTERPOLATION, 
                          java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(java.awt.RenderingHints.KEY_RENDERING, 
                          java.awt.RenderingHints.VALUE_RENDER_QUALITY);
        g.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, 
                          java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
        
        // 绘制缩略图
        g.drawImage(originalImage, 0, 0, thumbnailWidth, thumbnailHeight, null);
        g.dispose();
        
        // 创建缩略图文件
        String thumbnailFileName = FilenameUtils.getBaseName(originalFile.getName()) + 
                                 "_thumbnail_" + size + "." + 
                                 FilenameUtils.getExtension(originalFile.getName());
        File thumbnailFile = new File(originalFile.getParent(), thumbnailFileName);
        
        // 保存缩略图
        String formatName = getImageFormat(originalFile);
        ImageIO.write(thumbnailImage, formatName, thumbnailFile);
        
        return thumbnailFile;
    }
    
    /**
     * 获取文件的内容类型
     */
    private static String getFileContentType(File file) throws IOException {
        String fileName = file.getName().toLowerCase();
        if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
            return "image/jpeg";
        } else if (fileName.endsWith(".png")) {
            return "image/png";
        } else if (fileName.endsWith(".gif")) {
            return "image/gif";
        } else if (fileName.endsWith(".bmp")) {
            return "image/bmp";
        } else {
            // 尝试通过文件内容判断
            return Files.probeContentType(file.toPath());
        }
    }
    
    /**
     * 获取图片格式
     */
    private static String getImageFormat(File file) {
        String fileName = file.getName().toLowerCase();
        if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
            return "jpg";
        } else if (fileName.endsWith(".png")) {
            return "png";
        } else if (fileName.endsWith(".gif")) {
            return "gif";
        } else if (fileName.endsWith(".bmp")) {
            return "bmp";
        } else {
            return "jpg"; // 默认格式
        }
    }

	/**
	 * 输出指定文件的byte数组
	 * 
	 * @param filePath 文件路径
	 * @param os       输出流
	 * @return
	 */
	public static void writeBytes(String filePath, OutputStream os) throws IOException {
		FileInputStream fis = null;
		try {
			File file = new File(filePath);
			if (!file.exists()) {
				throw new FileNotFoundException(filePath);
			}
			fis = new FileInputStream(file);
			byte[] b = new byte[1024];
			int length;
			while ((length = fis.read(b)) > 0) {
				os.write(b, 0, length);
			}
		} catch (IOException e) {
			throw e;
		} finally {
			IOUtils.close(os);
			IOUtils.close(fis);
		}
	}

	/**
	 * 写数据到文件中
	 *
	 * @param data 数据
	 * @return 目标文件
	 * @throws IOException IO异常
	 */
	public static String writeImportBytes(byte[] data) throws IOException {
		return writeBytes(data, ProjectConfig.getImportPath());
	}

	/**
	 * 写数据到文件中
	 *
	 * @param data      数据
	 * @param uploadDir 目标文件
	 * @return 目标文件
	 * @throws IOException IO异常
	 */
	public static String writeBytes(byte[] data, String uploadDir) throws IOException {
		FileOutputStream fos = null;
		String pathName = "";
		try {
			String extension = getFileExtendName(data);
			pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
			File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
			fos = new FileOutputStream(file);
			fos.write(data);
		} finally {
			IOUtils.close(fos);
		}
		return FileUploadUtils.getPathFileName(uploadDir, pathName);
	}

	/**
	 * 移除路径中的请求前缀片段
	 * 
	 * @param filePath 文件路径
	 * @return 移除后的文件路径
	 */
	public static String stripPrefix(String filePath) {
		return StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
	}

	/**
	 * 删除文件
	 * 
	 * @param filePath 文件
	 * @return
	 */
	public static boolean deleteFile(String filePath) {
		boolean flag = false;
		File file = new File(filePath);
		// 路径为文件且不为空则进行删除
		if (file.isFile() && file.exists()) {
			flag = file.delete();
		}
		return flag;
	}

	/**
	 * 文件名称验证
	 * 
	 * @param filename 文件名称
	 * @return true 正常 false 非法
	 */
	public static boolean isValidFilename(String filename) {
		return filename.matches(FILENAME_PATTERN);
	}

	/**
	 * 检查文件是否可下载
	 * 
	 * @param resource 需要下载的文件
	 * @return true 正常 false 非法
	 */
	public static boolean checkAllowDownload(String resource) {
		// 禁止目录上跳级别
		if (StringUtils.contains(resource, "..")) {
			return false;
		}

		// 检查允许下载的文件规则
		if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) {
			return true;
		}

		// 不在允许下载的文件规则
		return false;
	}

	/**
	 * 下载文件名重新编码
	 * 
	 * @param request  请求对象
	 * @param fileName 文件名
	 * @return 编码后的文件名
	 */
	public static String setFileDownloadHeader(HttpServletRequest request, String fileName)
			throws UnsupportedEncodingException {
		final String agent = request.getHeader("USER-AGENT");
		String filename = fileName;
		if (agent.contains("MSIE")) {
			// IE浏览器
			filename = URLEncoder.encode(filename, "utf-8");
			filename = filename.replace("+", " ");
		} else if (agent.contains("Firefox")) {
			// 火狐浏览器
			filename = new String(fileName.getBytes(), "ISO8859-1");
		} else if (agent.contains("Chrome")) {
			// google浏览器
			filename = URLEncoder.encode(filename, "utf-8");
		} else {
			// 其它浏览器
			filename = URLEncoder.encode(filename, "utf-8");
		}
		return filename;
	}

	/**
	 * 下载文件名重新编码
	 *
	 * @param response     响应对象
	 * @param realFileName 真实文件名
	 * @return
	 */
	public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName)
			throws UnsupportedEncodingException {
		String percentEncodedFileName = percentEncode(realFileName);

		StringBuilder contentDispositionValue = new StringBuilder();
		contentDispositionValue.append("attachment; filename=").append(percentEncodedFileName).append(";")
				.append("filename*=").append("utf-8''").append(percentEncodedFileName);

		response.setHeader("Content-disposition", contentDispositionValue.toString());
	}

	/**
	 * 百分号编码工具方法
	 *
	 * @param s 需要百分号编码的字符串
	 * @return 百分号编码后的字符串
	 */
	public static String percentEncode(String s) throws UnsupportedEncodingException {
		String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
		return encode.replaceAll("\\+", "%20");
	}

	/**
	 * 获取图像后缀
	 * 
	 * @param photoByte 图像数据
	 * @return 后缀名
	 */
	public static String getFileExtendName(byte[] photoByte) {
		String strFileExtendName = "jpg";
		if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
				&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
			strFileExtendName = "gif";
		} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
			strFileExtendName = "jpg";
		} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
			strFileExtendName = "bmp";
		} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
			strFileExtendName = "png";
		}
		return strFileExtendName;
	}

	/**
	 * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
	 * 
	 * @param fileName 路径名称
	 * @return 没有文件路径的名称
	 */
	public static String getName(String fileName) {
		if (fileName == null) {
			return null;
		}
		int lastUnixPos = fileName.lastIndexOf('/');
		int lastWindowsPos = fileName.lastIndexOf('\\');
		int index = Math.max(lastUnixPos, lastWindowsPos);
		return fileName.substring(index + 1);
	}

	/**
	 * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
	 * 
	 * @param fileName 路径名称
	 * @return 没有文件路径和后缀的名称
	 */
	public static String getNameNotSuffix(String fileName) {
		if (fileName == null) {
			return null;
		}
		String baseName = FilenameUtils.getBaseName(fileName);
		return baseName;
	}

	/**
	 * 格式化文件大小，自动转换为合适的单位
	 * 
	 * @param size 文件大小（字节数）
	 * @return 格式化后的文件大小字符串，如：1.5 KB、2.3 MB、1.2 GB
	 */
	public static String formatFileSize(long size) {
		if (size <= 0) {
			return "0 B";
		}
		
		final String[] units = new String[]{"B", "KB", "MB", "GB", "TB", "PB", "EB"};
		int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
		
		if (digitGroups >= units.length) {
			digitGroups = units.length - 1;
		}
		
		return String.format("%.2f %s", size / Math.pow(1024, digitGroups), units[digitGroups]);
	}
    
	/**
	 * 格式化文件大小，指定小数位数
	 * 
	 * @param size 文件大小（字节数）
	 * @param decimalPlaces 小数位数
	 * @return 格式化后的文件大小字符串
	 */
	public static String formatFileSize(long size, int decimalPlaces) {
		if (size <= 0) {
			return "0 B";
		}
		
		final String[] units = new String[]{"B", "KB", "MB", "GB", "TB", "PB", "EB"};
		int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
		
		if (digitGroups >= units.length) {
			digitGroups = units.length - 1;
		}
		
		String format = "%." + decimalPlaces + "f %s";
		return String.format(format, size / Math.pow(1024, digitGroups), units[digitGroups]);
	}
}
